package drds.plus.sql_process.abstract_syntax_tree.node.dml;

import drds.plus.common.jdbc.Parameters;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.sql_process.abstract_syntax_tree.configuration.TableMetaData;
import drds.plus.sql_process.abstract_syntax_tree.expression.NullValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.bind_value.BindValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.bind_value.SequenceValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.BooleanFilter;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Function;
import drds.plus.sql_process.abstract_syntax_tree.node.Node;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Query;
import drds.plus.sql_process.abstract_syntax_tree.node.query.TableQuery;
import drds.plus.sql_process.optimizer.OptimizerContext;
import drds.plus.sql_process.optimizer.OptimizerException;
import drds.plus.sql_process.optimizer.pre_processor.SequencePreProcessor;
import drds.plus.sql_process.optimizer.pre_processor.SubQueryPreProcessor;
import drds.plus.sql_process.utils.OptimizerUtils;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

public abstract class Dml<T extends Dml> extends Node<T> {
    @Setter
    @Getter
    protected boolean needBuild = true;
    //
    @Setter
    @Getter
    protected boolean processAutoIncrement = true;
    @Setter
    @Getter
    protected List<Item> columnNameList;
    @Setter
    @Getter
    protected List<Object> columnValueList;
    @Setter
    @Getter
    protected boolean isValueListList = false;
    @Setter
    @Getter
    protected List<List<Object>> valueListList;
    /**
     * 这个节点上执行哪些batch
     *
     * @return
     */
    @Setter
    @Getter
    protected List<Integer> batchIndexList = new ArrayList<Integer>();
    // 直接依赖为tableNode，如果涉及多库操作，会是一个Merge下面挂多个DML
    @Setter
    @Getter
    protected TableQuery where = null;
    @Setter
    @Getter
    // insert into ... query
    protected Query query;
    @Setter
    @Getter
    protected Parameters parameters = null;

    public Dml() {
        super();
    }

    public Dml(TableQuery where) {
        this.where = where;
    }

    public void setParameters(Parameters parameters) {
        this.parameters = parameters;

    }

    public void setWhere(TableQuery where) {
        this.where = where;

    }

    public void setColumnNameList(List<Item> columnNameList) {
        this.columnNameList = columnNameList;

    }


    public void setColumnValueList(List<Object> columnValueList) {
        this.columnValueList = columnValueList;

    }

    public TableMetaData getTableMetaData() {
        return getWhere().getTableMetaData();
    }


    public void setIsValueListList(boolean isValueListList) {
        this.isValueListList = isValueListList;

    }

    public void setValueListList(List<List<Object>> valueListList) {
        this.valueListList = valueListList;

    }

    public List<Object> getValueList(int index) {
        if (this.isValueListList) {
            if (this.valueListList != null) {
                return this.valueListList.get(index);
            } else {
                return null;
            }
        }

        return this.columnValueList;
    }

    public int getValueListListSize() {
        if (this.isValueListList) {
            return this.valueListList.size();
        } else {
            return 1;
        }
    }

    public boolean processAutoIncrement() {
        return processAutoIncrement;
    }

    public void build() {
        if (this.where != null) {
            where.build();
        }

        if (this.query != null) {
            query.build();
        }

        // 先判断是否需要needAutoIncrement
        boolean needAutoIncrement = false;
        Item autoIncrementItem = null;
        if (processAutoIncrement()) {
            for (ColumnMetaData columnMetaData : getWhere().getTableMetaData().getOrderByColumnMetaDataList()) {
                if (columnMetaData.isAutoIncrement()) {
                    needAutoIncrement = true;
                    autoIncrementItem = OptimizerUtils.columnMetaDataToColumn(columnMetaData);
                }
            }
        }

        if (columnNameList == null || columnNameList.isEmpty()) { // 如果字段为空，默认为所有的字段数据的,比如insert所有字段
            columnNameList = OptimizerUtils.columnMetaListToIColumnList(this.getTableMetaData().getOrderByColumnMetaDataList(), this.getTableMetaData().getTableName());
            needAutoIncrement = false;
        } else {
            for (Item item : columnNameList) {
                ColumnMetaData columnMetaData = getWhere().getTableMetaData().getColumnMetaData(item.getColumnName());
                if (columnMetaData == null) {
                    throw new IllegalArgumentException("column: " + item.getFullName() + " is not existed in " + this.getWhere().getName());
                }
                item.setType(columnMetaData.getType());
                item.setTableName(columnMetaData.getTableName());
                item.setAutoIncrement(columnMetaData.isAutoIncrement());

                if (item.isAutoIncrement()) {
                    needAutoIncrement = false;
                }
            }

            if (needAutoIncrement) { // 添加自增列
                columnNameList.add(autoIncrementItem);
            }
        }

        boolean isMatch = true;
        if (isValueListList) {
            for (List<Object> valueList : valueListList) {
                if (needAutoIncrement) { // 添加自增列
                    valueList.add(ObjectCreateFactory.createSequenceValue(autoIncrementItem.getTableName()));
                }
                for (Object value : valueList) {
                    if (value instanceof SequenceValue) {
                        existSequenceValue = true;
                    }
                }
                isMatch &= (valueList.size() == columnNameList.size());
            }
        } else if (columnValueList != null) {
            if (needAutoIncrement) { // 添加自增列
                columnValueList.add(ObjectCreateFactory.createSequenceValue(autoIncrementItem.getTableName()));
            }

            isMatch &= (columnValueList.size() == columnNameList.size());
            for (Object object : columnValueList) {
                if (object instanceof SequenceValue) {
                    existSequenceValue = true;
                }
            }
        } else if (query != null) {
            List<Item> selectItemList = query.getSelectItemList();
            // insert into ... select语法，检查下列是否匹配
            if (processAutoIncrement()) {
                // 分库分表情况，不允许出现insert into where(id) ...
                // query null列的情况
                int index = -1;
                for (int i = 0; i < columnNameList.size(); i++) {
                    if (columnNameList.get(0).isAutoIncrement()) {
                        index = i;
                        break;
                    }
                }

                if (index >= 0) {
                    if (selectItemList.size() > index) {
                        Item item = selectItemList.get(index);
                        if (item instanceof BooleanFilter && ((BooleanFilter) item).getColumn() instanceof NullValue) {
                            throw new OptimizerException("insert into selectStatement not support selectStatement auto_increment is null");
                        }
                    }
                }
            }

            List<String> shardColumnNameList = OptimizerContext.getOptimizerContext().getRouteOptimizer().getShardColumnNameList(getTableMetaData().getTableName());
            if (!shardColumnNameList.isEmpty()) {
                // 校验下insert/select中的分区键不会做修改，需要保持一致
                for (String shardColumnName : shardColumnNameList) {
                    int index = -1;
                    for (int i = 0; i < columnNameList.size(); i++) {
                        Item column = columnNameList.get(i);
                        if (column.getColumnName().equals(shardColumnName)) {// 对应的分库键
                            index = i;
                            break;
                        }
                    }

                    if (index == -1) {
                        throw new OptimizerException("parition column " + shardColumnName + " is not found");
                    }

                    if (selectItemList.size() > index) {
                        Item item = selectItemList.get(index);
                        if (!(item instanceof Column)) {// 对应的分库键的列，必须是column列，不能是常量或者是函数
                            throw new OptimizerException("insert into selectStatement only support selectStatement is parition column");
                        }
                    }
                }
            }

            isMatch &= (columnNameList.size() == query.getSelectItemList().size());
        }

        if (!isMatch) {
            throw new OptimizerException("ColumnImpl count doesn't ruleCalculate value count");
        }

        for (Item item : this.getColumnNameList()) {
            Item res = null;
            for (Object object : where.copyReferedItemList()) {
                Item item1 = (Item) object;
                if (item.isSameColumnName(item1)) { // 尝试查找对应的字段信息
                    res = item1;
                    break;
                }
            }

            if (res == null) {
                throw new IllegalArgumentException("column: " + item.getColumnName() + " is not existed in " + where.getName());
            }
            item.setType(res.getType());
        }

        if (isValueListList) {
            for (List<Object> valueList : valueListList) {
                convertByValueType(this.getColumnNameList(), valueList);
            }
        } else if (columnValueList != null) {
            convertByValueType(this.getColumnNameList(), columnValueList);
        }
    }

    /**
     * 尝试根据字段类型进行value转化
     */
    protected List<Object> convertByValueType(List<Item> itemList, List<Object> valueList) {
        for (int i = 0; i < valueList.size(); i++) {
            Item item = itemList.get(i);
            Object value = valueList.get(i);
            if (item.isAutoIncrement()) {
                // 自增id必须是个具体的值或者values()函数
                if (value instanceof Column) {
                    throw new OptimizerException("unkonw sequence parser : " + value.toString());
                }
            }
            valueList.set(i, OptimizerUtils.convertType(value, item.getType()));
        }
        return valueList;
    }

    public void setDataNodeId(String dataNode) {
        super.setDataNodeId(dataNode);

    }

    public void assignment(Parameters parameters) {
        if (where != null) {
            where.assignment(parameters);
        }

        if (query != null) {
            query.assignment(parameters);
        }

        if (columnValueList != null) {
            this.setColumnValueList(assignmentValues(columnValueList, parameters));
        }

        if (valueListList != null) {
            List<List<Object>> newValueListList = new ArrayList<List<Object>>(this.valueListList.size());
            for (List<Object> valueList : this.valueListList) {
                newValueListList.add(assignmentValues(valueList, parameters));
            }

            this.setValueListList(newValueListList);
        }
    }

    /**
     * 尝试根据字段类型进行value转化
     */
    protected List<Object> assignmentValues(List<Object> valueList, Parameters parameters) {
        List<Object> newValueList = new ArrayList<Object>(valueList.size());
        for (int i = 0; i < valueList.size(); i++) {
            Object value = valueList.get(i);
            Item item = columnNameList.get(i);
            if (processAutoIncrement()) {
                if (item.isAutoIncrement() && !(value instanceof SequenceValue)) {
                    value = SequencePreProcessor.convertToSequence(item, value, parameters);
                }
            }

            if (value instanceof BindValue) {
                newValueList.add(((BindValue) value).assignment(parameters));
            } else if (value instanceof Item) {
                newValueList.add(((Item) value).assignment(parameters));
            } else {
                newValueList.add(value);
            }
        }

        return newValueList;
    }

    public Function getNextSubQueryFunction() {
        Function function = SubQueryPreProcessor.getNextSubQueryFunction(this.getWhere());
        if (function != null) {
            return (Function) function.copy();
        } else {
            return null;
        }
    }


    protected void copySelfTo(Dml to) {
        to.columnNameList = this.columnNameList;
        to.columnValueList = this.columnValueList;
        to.where = this.where;
        to.query = this.query;
        to.valueListList = this.valueListList;
        to.isValueListList = this.isValueListList;
        to.existSequenceValue = this.existSequenceValue;

        to.batchIndexList = this.batchIndexList;
        to.processAutoIncrement = this.processAutoIncrement;
    }

    protected void deepCopySelfTo(Dml to) {
        to.columnNameList = OptimizerUtils.copySelectItemList(this.columnNameList);
        if (this.columnValueList != null) {
            to.columnValueList = new ArrayList(this.columnValueList.size());
            for (Object value : this.columnValueList) {
                if (value instanceof Item) {
                    to.columnValueList.add(((Item) value).copy());
                } else if (value instanceof BindValue) {
                    to.columnValueList.add(((BindValue) value).copy());
                } else {
                    to.columnValueList.add(value);
                }
            }
        }

        to.setIsValueListList(this.isValueListList());
        if (this.valueListList != null) {
            List<List<Object>> multiValues = new ArrayList<List<Object>>(this.valueListList.size());
            for (List<Object> value : this.valueListList) {
                multiValues.add(OptimizerUtils.copyValues(value));
            }

            to.setValueListList(multiValues);
        }

        to.where = this.where.deepCopy();
        if (this.getQuery() != null) {
            to.query = this.query.deepCopy();
        }
        to.existSequenceValue = this.existSequenceValue;

        to.batchIndexList = new ArrayList<Integer>(this.batchIndexList);
        to.processAutoIncrement = this.processAutoIncrement;
    }

    public T copySelf() {
        return copy();
    }


}
